﻿using System;
using System.Threading;
using System.Threading.Tasks;
using housingsafety.server.Caching.Core;
using StackExchange.Redis;

namespace housingsafety.server.Caching.Redis

{
    public class RedisService : IDisposable
    {
        private ConnectionMultiplexer m_Connection;
        private IDatabase m_Database;
        private string m_Configuration;

        public IDatabase Database
        {
            get { return m_Database; }
        }

        public RedisService(string configuration)
        {
            Requires.NotNullOrEmpty(configuration, nameof(configuration));
            m_Configuration = configuration;
        }

        public void Set(string key, HashEntry[] values)
        {
            Requires.NotNullOrEmpty(key, nameof(key));
            Requires.NotNull(values, nameof(values));
            Connect();
            m_Database.HashSet(key, values);
        }

        public async Task SetAsync(string key, HashEntry[] values)
        {
            Requires.NotNullOrEmpty(key, nameof(key));
            Requires.NotNull(values, nameof(values));
            await ConnectAsync();
            await m_Database.HashSetAsync(key, values);
        }

        public void Remove(string key)
        {
            Requires.NotNullOrEmpty(key, nameof(key));
            Connect();
            m_Database.KeyDelete(key);
        }

        public async Task RemoveAsync(string key, CancellationToken token = new CancellationToken())
        {
            Requires.NotNullOrEmpty(key, nameof(key));
            await ConnectAsync();
            await m_Database.KeyDeleteAsync(key);
        }

        public HashEntry[] HashGet(string key, string member = null)
        {
            Requires.NotNullOrEmpty(key, nameof(key));
            Connect();
            if (member != null)
                return new[] { new HashEntry(member, m_Database.HashGet(key, member)) };

            return m_Database.HashGetAll(key);
        }

        public async Task<HashEntry[]> HashGetAsync(string key, string member = null)
        {
            Requires.NotNullOrEmpty(key, nameof(key));
            await ConnectAsync();
            if (member != null)
                return new[] { new HashEntry(member, await m_Database.HashGetAsync(key, member)) };

            return await m_Database.HashGetAllAsync(key);
        }

        public void KeyExpire(string key, TimeSpan? expr)
        {
            Requires.NotNullOrEmpty(key, nameof(key));
            Connect();
            m_Database.KeyExpire(key, expr);
        }

        public async Task KeyExpireAsync(string key, TimeSpan? expr)
        {
            Requires.NotNullOrEmpty(key, nameof(key));
            await ConnectAsync();
            await m_Database.KeyExpireAsync(key, expr);
        }

        public void Dispose()
        {
            if (m_Connection != null)
            {
                m_Connection.Dispose();
            }
        }

        public void Connect()
        {
            if (m_Connection == null)
            {
                m_Connection = ConnectionMultiplexer.Connect(m_Configuration);
                m_Database = m_Connection.GetDatabase();
            }
        }

        public async Task ConnectAsync()
        {
            if (m_Connection == null)
            {
                m_Connection = await ConnectionMultiplexer.ConnectAsync(m_Configuration);
                m_Database = m_Connection.GetDatabase();
            }
        }
    }
}
